package com.hospital.wx.util;

/*
* 生成雪花算法工具类
* */
public class SnowflakeIdGenerator {
        // 起始时间戳，这里以 2020-01-01 00:00:00 为例
        private final long startTimeStamp = 1577836800000L;
        // 机器 ID 所占的位数
        private final long workerIdBits = 5L;
        // 数据中心 ID 所占的位数
        private final long dataCenterIdBits = 5L;
        // 序列号所占的位数
        private final long sequenceBits = 12L;

        // 机器 ID 向左移位数
        private final long workerIdShift = sequenceBits;
        // 数据中心 ID 向左移位数
        private final long dataCenterIdShift = sequenceBits + workerIdBits;
        // 时间戳向左移位数
        private final long timestampShift = sequenceBits + workerIdBits + dataCenterIdBits;

        // 序列号掩码
        private final long sequenceMask = -1L ^ (-1L << sequenceBits);

        private final long workerId;
        private final long dataCenterId;
        private long sequence = 0L;
        private long lastTimestamp = -1L;

        public SnowflakeIdGenerator(long workerId, long dataCenterId) {
            // 机器 ID 范围检查
            long maxWorkerId = -1L ^ (-1L << workerIdBits);
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException("Worker ID 超出范围");
            }
            // 数据中心 ID 范围检查
            long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);
            if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
                throw new IllegalArgumentException("Data Center ID 超出范围");
            }
            this.workerId = workerId;
            this.dataCenterId = dataCenterId;
        }

        public synchronized long nextId() {
            long currentTimestamp = System.currentTimeMillis();
            // 处理时钟回拨问题
            if (currentTimestamp < lastTimestamp) {
                throw new RuntimeException("时钟回拨，拒绝生成 ID " + (lastTimestamp - currentTimestamp) + " 毫秒");
            }
            if (currentTimestamp == lastTimestamp) {
                sequence = (sequence + 1) & sequenceMask;
                if (sequence == 0) {
                    // 序列号溢出，等待下一毫秒
                    currentTimestamp = waitNextMillis(lastTimestamp);
                }
            } else {
                sequence = 0L;
            }
            lastTimestamp = currentTimestamp;
            return ((currentTimestamp - startTimeStamp) << timestampShift) |
                    (dataCenterId << dataCenterIdShift) |
                    (workerId << workerIdShift) |
                    sequence;
        }

        private long waitNextMillis(long lastTimestamp) {
            long timestamp = System.currentTimeMillis();
            while (timestamp <= lastTimestamp) {
                timestamp = System.currentTimeMillis();
            }
            return timestamp;
        }

        public static void main(String[] args) {
            SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
            for (int i = 0; i < 10; i++) {
                System.out.println(idGenerator.nextId());
            }
        }
}
